﻿// Shader-Based-Image-Processing (SBIP)
// http://code.google.com/p/sbip/
//
// Copyright © Frank Nagl, 2009-2011
// admin@franknagl.de
//
namespace SBIP.Filter.NonSBIP
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using Helper;

    /// <summary>
    /// This filter eliminates regions of a <see cref="EliminateColor">specified 
    /// color</see> by growing neighbour regions with other colors together. The
    /// region growing mechnism works exclusively in horicontal direction.
    /// </summary>
    public class NSHoricontalColorEliminatedRegionGrowing : BaseNonSBIPFilter
    {
        /// <summary>
        /// Describes the growing order to eliminate the 
        /// <see cref="NSHoricontalColorEliminatedRegionGrowing.EliminateColor">
        /// specified color</see> in horicontal direction.
        /// </summary>
        public enum HoricontalGrowingOrder
        {
            /// <summary>It grows equal from left and right to the middle.</summary>
            FromLeftAndRightToMiddle,
            /// <summary>It grows from left side to right side.</summary>
            FromLeftToRight,
            /// <summary>It grows from right side to left side.</summary>
            FromRightToLeft
        }
      
        /// <summary>
        /// Describes the allowed deviation of every channel for 
        /// <see cref="EliminateColor"/>. Default: [25, 25, 25].
        /// </summary>
        public Color Deviation { get; set; }

        /// <summary>Color, that will be eliminated by growing together the
        /// neighbour regions of pixels with this color.</summary>
        public Color EliminateColor { get; set; }

        /// <summary>
        /// Describes the growing order to eliminate the 
        /// <see cref="EliminateColor">specified color</see>.
        /// </summary>
        public HoricontalGrowingOrder GrowingOrder { get; set; }

        /// <summary>
        /// Initializes a new instance of the 
        /// <see cref="NSColorEliminatedRegionGrowing"/> class.
        /// </summary>
        public NSHoricontalColorEliminatedRegionGrowing()
        {
            SupportedSrcPixelFormats = PixelFormatFlags.Format24BppRgb;
            Deviation = Color.FromArgb(25, 25, 25);
        }

        /// <summary>
        /// Extracts the color segments of the original bitmap as pixel-connected regions.
        /// </summary>
        protected override unsafe void Process(BitmapData srcData, BitmapData dstData)
        {
            const int ps = 3; // Image.GetPixelFormatSize(srcData.PixelFormat) / 8;
            int w = srcData.Width;
            int h = srcData.Height;
            int s = srcData.Stride;
            int offset = s - w * ps;

            byte* src = (byte*)srcData.Scan0.ToPointer();
            byte* dst = (byte*)dstData.Scan0.ToPointer();

            // copy src to dst            
            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++, src += ps, dst += ps)
                {
                    dst[RGBA.R] = src[RGBA.R];
                    dst[RGBA.G] = src[RGBA.G];
                    dst[RGBA.B] = src[RGBA.B];
                }
               //dst += offset; // do not do this offset addition
            }

            // start filter process
            List<Point> coords = new List<Point>(w);
            dst = (byte*)dstData.Scan0.ToPointer();
            // for each line
            for (int y = 0; y < h; y++)
            {
                bool lineStart = true;
                bool started = false;
                coords.Clear();
                // color values on left side
                byte r1 = 0, g1 = 0, b1 = 0;
                // for each pixel
                for (int x = 0; x < w; x++, dst+= ps)
                {
                    if (Math.Abs(dst[RGBA.R] - EliminateColor.R) < Deviation.R &&
                        Math.Abs(dst[RGBA.G] - EliminateColor.G) < Deviation.G &&
                        Math.Abs(dst[RGBA.B] - EliminateColor.B) < Deviation.B)
                    {
                        if (lineStart)
                        {
                            continue;
                        }
                        if (!started)
                        {
                            started = true;
                        }
                        coords.Add(new Point(x, y));
                    }
                    else
                    {
                        if (lineStart)
                        {
                            lineStart = false;
                        }

                        if (started)
                        {
                            switch (GrowingOrder)
                            {
                                case HoricontalGrowingOrder.FromLeftAndRightToMiddle:
                                    // left side of region
                                    for (int i = 0; i < coords.Count / 2; i++)
                                    {
                                        byte* ptr = (byte*)dstData.Scan0.ToPointer();
                                        ptr += s * coords[i].Y + ps * coords[i].X;
                                        ptr[RGBA.R] = r1;
                                        ptr[RGBA.G] = g1;
                                        ptr[RGBA.B] = b1;
                                    }
                                    // right side of region
                                    for (int i = coords.Count / 2; i < coords.Count; i++)
                                    {
                                        byte* ptr = (byte*)dstData.Scan0.ToPointer();
                                        ptr += s * coords[i].Y + ps * coords[i].X;
                                        ptr[RGBA.R] = dst[RGBA.R];
                                        ptr[RGBA.G] = dst[RGBA.G];
                                        ptr[RGBA.B] = dst[RGBA.B];
                                    }
                                    break;
                                case HoricontalGrowingOrder.FromLeftToRight:
                                    foreach (Point c in coords)
                                    {
                                        byte* ptr = (byte*)dstData.Scan0.ToPointer();
                                        ptr += s * c.Y + ps * c.X;
                                        ptr[RGBA.R] = r1;
                                        ptr[RGBA.G] = g1;
                                        ptr[RGBA.B] = b1;
                                    }
                                    break;
                                case HoricontalGrowingOrder.FromRightToLeft:
                                    foreach (Point c in coords)
                                    {
                                        byte* ptr = (byte*)dstData.Scan0.ToPointer();
                                        ptr += s * c.Y + ps * c.X;
                                        ptr[RGBA.R] = dst[RGBA.R];
                                        ptr[RGBA.G] = dst[RGBA.G];
                                        ptr[RGBA.B] = dst[RGBA.B];
                                    }
                                    break;
                            }                            

                            coords.Clear();
                            started = false;
                        }
                        else
                        {                            
                            r1 = dst[RGBA.R];
                            g1 = dst[RGBA.G];
                            b1 = dst[RGBA.B];
                        }
                    }
                }
                dst += offset;
            }
        }
    }
}
